﻿using System;
using System.Collections.Generic;
using DataTier;
using DataAccessLayer;
using System.Text.RegularExpressions;

namespace BusinessTier
{
    public class BBooking : IBBooking
    {
        public List<Booking> getAllBookings(int sId)
        {
            return DBBooking.getAllBookings(sId);

        }


        public void AcceptRoute(Route route, Customer customer, Subscription subscription)
        {



            route.RouteId = DBBooking.AddRoute(route);

            IBBattery bBattery = new BBattery();


            

            foreach (Booking booking in route.Bookings)
            {
                DateTime lockedUntil = booking.BookingDate;
                lockedUntil = booking.BookingDate;
                lockedUntil = lockedUntil.AddMinutes(20);

                booking.IsCancelled = false;
                bBattery.LockBattery(booking.Battery.BatteryId, true, lockedUntil);
                DBBooking.AddBooking(booking, customer, subscription, route, lockedUntil);
            }


         
        }

        



        /// <summary>
        /// This method calculates the route.
        /// </summary>
        /// <param name="fromZip"></param>
        /// <param name="fromCity"></param>
        /// <param name="fromAdress"></param>
        /// <param name="dateIsStart"></param>
        /// <param name="date"></param>
        /// <param name="toZip"></param>
        /// <param name="toCity"></param>
        /// <param name="toAdress"></param>
        /// <returns>A fully filled route with bookings</returns>

        public Route CalculateRoute(int fromZip, string fromCity, string fromAdress, bool dateIsStart, DateTime date, int toZip, string toCity, string toAdress, 
            Customer customer, Subscription subscription)
        {

            BookingSweeper();

            DateTime startDate = new DateTime();
            DateTime endDate = new DateTime();
            if (dateIsStart == true)
            {
                startDate = date;
            }
            else
            {
                endDate = date;
            }
            Route route = new Route(fromZip, fromCity, fromAdress, startDate, toZip, toCity, toAdress, endDate);

            List<Station> connections = FindShortestRoute(fromZip, toZip).Connection;


            IBBattery bBattery = new BBattery();
            DateTime bookingDate = date;
            Station lastVisit = connections[0];
            int index = 2;


            double kmToNextCharge = (double)subscription.CarModel.KmOnFullCharge / 100 * 100;
            
      

            while(index - 1 < connections.Count)
            {

                

                if (connections[index - 1].PathSum - connections[index - 2].PathSum > kmToNextCharge - 20)
                {


                    double timeOnRoad = ((double)connections[index - 1].PathSum - (double)lastVisit.PathSum) / 80 * 60;
                    bookingDate = bookingDate.AddMinutes(timeOnRoad);

                        Booking booking = new Booking(bookingDate); 
                        if (FindBookingTimeInterval(booking, connections[index - 1]) == true)
                        {
                            booking.IsCollected = false;
                            booking.IsCancelled = true;

                            DateTime lockedUntil = DateTime.Now;
                            lockedUntil = lockedUntil.AddMinutes(5);

                            booking.Battery = bBattery.FindAvailableBattery(connections[index - 1].StationId, booking.BookingDate);
                            bBattery.LockBattery(booking.Battery.BatteryId, true, lockedUntil);
                            DBBooking.AddBooking(booking, customer, subscription, route, lockedUntil);
                            kmToNextCharge = (double)subscription.CarModel.KmOnFullCharge / 100 * 100;
                            lastVisit = connections[index - 2];

                      
                        }

                        bookingDate.AddMinutes(10);
                        route.Bookings.Add(booking);
               
                    
                }

                else if (connections[index - 1].PathSum - connections[index - 2].PathSum < kmToNextCharge - 20)
                {
                   kmToNextCharge = kmToNextCharge - (connections[index - 1].PathSum - connections[index - 2].PathSum);
                }

                if (connections[index - 1].PathSum - connections[index - 2].PathSum > kmToNextCharge - 20)
                {
                    index = index - 1;
                }

                
                index++;
            }

            double timeOnRoad2 = ((double)connections[index - 2].PathSum - (double)lastVisit.PathSum) / 80 * 60;
            route.EndDate = bookingDate.AddMinutes(timeOnRoad2);

            return route;
        }

        /// <summary>
        /// This methods finds the bookings around the timeslot the user want to book and finds a timeslot that is no occupied.
        /// </summary>
        /// <param name="dateTime"></param>
        /// <returns>Returns a booking in a timeslot that is not already booked</returns>

        public bool FindBookingTimeInterval(Booking booking, Station station)
        {

            List<Booking> bookings = DBBooking.FindBookingTimeInteval(booking.BookingDate, station.StationId);
            bool timeSlotAvailable = false;

            

            double minute = booking.BookingDate.Minute;
            booking.BookingDate = booking.BookingDate.AddMinutes(-minute);
            booking.BookingDate = booking.BookingDate.AddMinutes(Math.Ceiling(minute / 5) * 5);
            
            int bookingCount = bookings.Count;

            if (bookings.Count == 0)
            {
                timeSlotAvailable = true;
            }

            while(timeSlotAvailable == false)
            {
                
                string bookingToCompare = booking.BookingDate.ToString("yyyy-MM-dd HH:mm");
             
                List<Booking> lockedBookings = new List<Booking>();

                foreach (Booking booking2 in bookings)
                {
                    string bookingInExistence = booking2.BookingDate.ToString("yyyy-MM-dd HH:mm");

                    if (bookingToCompare == bookingInExistence)
                    {
                        lockedBookings.Add(booking2);
                    }
                    
                }

                if (lockedBookings.Count != 0)
                {
                    booking.BookingDate = booking.BookingDate.AddMinutes(5);
                }

                else
                {
                    timeSlotAvailable = true;
                }

            }
            return timeSlotAvailable;
        }


        /// <summary>
        /// Finds the shortest way from one zipCode to another. 
        /// </summary>
        /// <param name="fromZip">The zipCode where you start.</param>
        /// <param name="toZip">The zipCode where you want to end up.</param>
        /// <returns>Returns the Station endStation object with the shortest connection list</returns>
        
        public Station FindShortestRoute(int fromZip, int toZip)
        {
            BStation bStation = new BStation();

            List<Station> stations = bStation.GetAllStations();


            int index = 0;
            bool found = false;
            Station currentStation = null;
            Station endStation = null;

            while (found != true)
            {
                if (fromZip == stations[index].ZipCode)
                {
                    currentStation = stations[index];

                    found = true;
                }
                else
                {
                    index++;
                }

            }

                index = 0;
                found = false;

                while (found != true)
                {
                    if (toZip == stations[index].ZipCode)
                    {
                        endStation = stations[index];
                        found = true;
                    }
                    else
                    {
                        index++;
                    }
                }

                    List<Station> markedStations = new List<Station>();
                    List<Station> stack = new List<Station>();
                    stack.Add(currentStation);
                    index = 0;
            
                    

                    while (markedStations.Count != stations.Count && stack != null)
                    {
                        
                        currentStation = stack[0];



                        foreach (Edge edge in currentStation.Edges)
                        {
                            if (edge.To.PathSum > edge.Cost + edge.From.PathSum || edge.To.PathSum == 0)
                            {

                                edge.To.Connection = new List<Station>(edge.From.Connection);


                                if (!edge.To.Connection.Contains(edge.From))
                                {
                                    edge.To.Connection.Add(edge.From);
                                }

                                if (edge.To.ZipCode != fromZip)
                                {
                                    edge.To.PathSum = edge.Cost + edge.From.PathSum;
                                }

                                if (!markedStations.Contains(edge.To))
                                {
                                    stack.Add(edge.To);
                                }


                            }


                        }

                        if (!markedStations.Contains(currentStation))
                        {
                            markedStations.Add(currentStation);
                        }

                        stack.Remove(currentStation);

                        
                        
                        
                    }

            endStation.Connection.Add(endStation);
            return endStation;


                }

          


        private void BookingSweeper()
        {
            IBBattery bBattery = new BBattery();
            DateTime sweepDate = DateTime.Now;
            DBBooking.BookingSweeper(sweepDate);
            bBattery.BatteryUnlockSweeper(sweepDate);

        }
            
        }
    }

    
    

